# -*- coding: utf-8 -*-
import hashlib
import json
import time
from datetime import datetime
from collections import OrderedDict

import requests
from django.conf import settings
from pyDes import des, ECB, PAD_PKCS5

from common.bankcard import db as bank_db
from common.bankcard.model import BANK_DCT
from common.mch import handler as mch_handler
from common.mch.db import get_account
from common.timer import TIMER_EVENT_TYPE
from common.timer.handler import submit_notify_event
from common.utils import track_logging
from common.utils.exceptions import ParamError
from common.withdraw import db as withdraw_db
from common.withdraw.model import WITHDRAW_ORDER_STATUS

_LOGGER = track_logging.getLogger('withdraw')

APP_CONF = {
    'SHID20181120600': {
        'API_KEY': '61372189-1026-23ec-10ee-0f6a0c429dcd',
        'GATEWAY':
            'https://api.gomepay.com/CoreServlet?aid={}&api_id={}&signature={}&timestamp={}&nonce={}&method=POST',
        'QUERY_GATEWAY':
            'https://api.gomepay.com/CoreServlet?aid={}&api_id={}&signature={}&timestamp={}&nonce={}&method=POST',
        'CHECKOUT_KEY': 'dc382a7d',  # 收银台密钥
        'AID': '8a179b8c65c9e6ce01673e977dbd389c',
        'APP_KEY': 'bsdakjl|m2|20181129',
    },
}


def hexString2bytes(src):
    ret = []
    for i in range(len(src) / 2):
        hd = int(src[i * 2:i * 2 + 1], 16)
        ld = int(src[i * 2 + 1:i * 2 + 2], 16)
        fd = (hd * 16 + ld) & 0xff
        ret.append(fd)
    return ret


def byte2hexString(byte_arr):
    ret = ''
    for i in range(len(byte_arr)):
        hx = hex(ord(byte_arr[i]))[2:]
        if len(hx) == 1:
            hx = '0' + hx
        ret += hx.upper()
    return ret


# DES ECB 解密
def des_ecb_decrypt(source, key):
    source = hexString2bytes(source)
    source = [chr(x) for x in source]
    des_obj = des(key.encode('utf-8'), ECB, IV=None, pad=None, padmode=PAD_PKCS5)
    des_result = des_obj.decrypt(source)
    return des_result


# DES ECB 加密
def des_ecb_encode(source, key):
    des_obj = des(key.encode('utf-8'), ECB, IV=None, pad=None, padmode=PAD_PKCS5)
    source = [chr(ord(x)) for x in source]
    des_result = des_obj.encrypt(source)
    return byte2hexString(des_result)


def _get_api_key(app_id):
    return APP_CONF[app_id]['API_KEY']


def _get_gateway(app_id):
    return APP_CONF[app_id]['GATEWAY']


def _get_query_gateway(app_id):
    return APP_CONF[app_id]['QUERY_GATEWAY']


def _get_checkout_key(app_id):
    return APP_CONF[app_id]['CHECKOUT_KEY']


def _get_app_key(app_id):
    return APP_CONF[app_id]['APP_KEY']


def _get_aid(app_id):
    return APP_CONF[app_id]['AID']


def generate_sign(parameter_list):
    parameter_list = sorted(parameter_list)
    s = ''
    for i in parameter_list:
        s += str(i)
    m = hashlib.sha1()
    m.update(s.encode('utf8'))
    sign = m.hexdigest()
    return sign


def _get_signature(*args):
    lst = []
    for i in args:
        if not i:
            i = ''
        lst.append(i)
    return lst


def _get_str_value(s, word):
    for i in s.split('&'):
        k, v = i.split('=')
        if k == word:
            return v


def create_order(order, channel):
    try:
        app_id = channel.appid
        mch = get_account(order.mch_id)
        sign_dct = OrderedDict((
            ("aid", _get_aid(app_id)),
            ("api_id", "eb_trans@agent_for_paying"),
            ("app_key", _get_app_key(app_id)),
            ("timestamp", int(time.time())),
            ("nonce", int(time.time()))
        ))
        parameter_dict = OrderedDict((
            # 全局参数
            ("login_token", ""),
            ("req_no", '_'.join([str(mch.name), str(order.id)])),
            ("app_code", "apc_02000003560"),
            ("app_version", "1.0.0"),
            ("service_code", "sne_00000000002"),
            ("plat_form", "03"),
            # 输入参数
            ("merchant_number", app_id),
            ("order_number", '_'.join([str(mch.name), str(order.id)])),
            ("wallet_id", "0100850744539963"),
            ("asset_id", "77638e8e1bd340beb24f3636de2a53d3"),
            ("business_type", "1"),
            ("money_model", "1"),
            ("source", "0"),
            ("password_type", "02"),
            ("encrypt_type", "02"),
            ("pay_password", "6CF82EE1020CAEF069E753C67A97A70D"),  # md5 加密
            ("customer_type", "01"),
            ("customer_name", order.pay_account_username),
            ("currency", "CNY"),
            ("amount", '%.2f' % float(order.amount)),
            ("async_notification_addr", settings.MEIFUBAO_WITHDRAW_NOTIFY_URL + app_id),
            ("account_number", order.pay_account_num),
            ("issue_bank_name", BANK_DCT[order.pay_account_bank])
        ))
        parameter_dict = json.dumps(parameter_dict, separators=(',', ':'))
        # aid, tid, api_id, key, timestamp, nonce, data_sign, null, null);
        lst = _get_signature(sign_dct['aid'], None, sign_dct['api_id'], sign_dct['app_key'],
                             sign_dct['timestamp'], sign_dct['nonce'], parameter_dict, None, None)
        signature = generate_sign(lst)
        # 先设置为三方未知状态
        order.status = WITHDRAW_ORDER_STATUS.THIRD_UNKNOWN
        order.third_type = channel.id
        order.save()
        url = _get_gateway(app_id).format(sign_dct['aid'], sign_dct['api_id'], signature, sign_dct['timestamp'],
                                          sign_dct['nonce'])
        _LOGGER.info('meifubao_withdraw create data: %s, %s', parameter_dict, url)
        headers = {'Content-type': 'application/json', 'Connection': 'close'}
        response = requests.post(url, headers=headers, data=parameter_dict, timeout=5, verify=False)
        _LOGGER.info('meifubao_withdraw create rsp: %s, %s', response.text, order.id)
        data = json.loads(response.text)
        third_id = data.get('orderid', None)
        status = data.get('op_ret_code', None)
        if status == "000" and third_id:
            order.status = WITHDRAW_ORDER_STATUS.THIRD
            order.third_id = str(third_id)
            order.extra_info = response.content
            order.save()
            return True
        else:
            order.detail = data.get('error', '')
            order.status = WITHDRAW_ORDER_STATUS.THIRD_FAIL
            order.extra_info = response.content
            order.save()
            return False
    except Exception, e:
        _LOGGER.info('meifubao_withdraw create order fail: %s, %s', e, order.id)
        return False


def verify_notify_sign(params, key):
    dstbdata = params['dstbdata'].encode('utf-8')  # 签名明文
    dstbdatasign = params['dstbdatasign'].encode('utf-8')  # 签名密文
    _LOGGER.info('dstbdata: %s, dstbdatasign: %s', dstbdata, dstbdatasign)
    calculated_sign = des_ecb_encode(dstbdata, key)
    _LOGGER.info('meifubao_withdraw calculated_sign: %s', calculated_sign)
    if dstbdatasign != calculated_sign:
        _LOGGER.info("meifubao_withdraw sign: %s, calculated sign: %s", dstbdatasign, calculated_sign)
        raise ParamError('sign not pass, data: %s' % params)


def check_notify_sign(request, app_id):
    checkout_key = _get_checkout_key(app_id)
    data = dict(request.POST.iteritems())
    _LOGGER.info("meifubao_withdraw notify data: %s", data)
    verify_notify_sign(data, checkout_key)
    data = data['dstbdata']  # 明文, 字符串
    pay_id = int(_get_str_value(data, 'dsorderid').split('_')[1])
    if not pay_id:
        _LOGGER.error("meifubao_withdraw fatal error, out_trade_no not exists, data: %s", data)
        raise ParamError('meifubao_withdraw event does not contain pay ID')
    order = withdraw_db.get_order(pay_id)
    if not order or order.status != WITHDRAW_ORDER_STATUS.THIRD:
        _LOGGER.info('meifubao_withdraw notify order not valid: %s ', pay_id)
        raise ParamError('meifubao_withdraw event order not valid: %s' % pay_id)

    if _get_str_value(data, 'returncode') != '00':
        order.status = WITHDRAW_ORDER_STATUS.THIRD_FAIL
        order.extra_info = request.body
        order.third_notify_at = datetime.utcnow()
        order.save()
        return
    order.status = WITHDRAW_ORDER_STATUS.DONE
    order.extra_info = request.body
    order.third_notify_at = datetime.utcnow()
    order.save()
    _LOGGER.info("meifubao_withdraw success mch_id: %s, order_id: %s", order.mch_id, order.id)
    notify_success, _ = mch_handler.notify_mch_withdraw(order)
    if not notify_success:
        submit_notify_event(order, TIMER_EVENT_TYPE.MCH_WITHDRAW_NOTIFY)


def query_order(app_id, pay_id):
    withdraw = withdraw_db.get_order(pay_id)
    mch = get_account(withdraw.mch_id)
    sign_dct = OrderedDict((
        ("aid", _get_aid(app_id)),
        ("api_id", "eb_trans@get_order_deal_result"),
        ("app_key", _get_app_key(app_id)),
        ("timestamp", int(time.time())),
        ("nonce", int(time.time()))
    ))
    parameter_dict = OrderedDict((
        # 全局参数
        ("login_token", ""),
        ("req_no", '_'.join([str(mch.name), str(pay_id)])),
        ("app_code", "apc_02000003560"),
        ("app_version", "1.0.0"),
        ("service_code", "sne_00000000002"),
        ("plat_form", "03"),
        # 输入参数
        ("merchant_number", app_id),
        ("order_number", '_'.join([str(mch.name), str(pay_id)])),
        ("deal_type", "07"),  # 代付07
    ))
    parameter_dict = json.dumps(parameter_dict, separators=(',', ':'))
    lst = _get_signature(sign_dct['aid'], None, sign_dct['api_id'], sign_dct['app_key'],
                         sign_dct['timestamp'], sign_dct['nonce'], parameter_dict, None, None)
    signature = generate_sign(lst)
    url = _get_gateway(app_id).format(sign_dct['aid'], sign_dct['api_id'], signature, sign_dct['timestamp'],
                                      sign_dct['nonce'])
    _LOGGER.info('meifubao_withdraw query create data: %s, %s', parameter_dict, url)
    headers = {'Content-type': 'application/json'}
    response = requests.post(url, headers=headers, data=parameter_dict, timeout=5)
    _LOGGER.info('meifubao_withdraw query rsp, %s', response.text)
    """
    response: {
        'amount': '1.10',  # 代付金额
        'balance': 0.7,  # 账户余额
        'currency': 'CNY',
        'deal_time': '2018-11-28 14:44:43',  # 交易时间
        'in_order_id': None,  # 当订单交易结果查询为转账订单时，此字段必填, order_id代表转出订单号, in_order_id代表转入订单号
        'merchant_number': 'SHID20181120600',  # 我方商户号
        'op_err_msg': '成功',  # 信息
        'op_err_obj': '订单对账: get_order_deal_result',  
        'op_ret_code': '000',  # 000:成功, 610:请求数据错误, 600:交易失败, 701:处理中, 702:订单未付款, 730:订单不存在, 500:其他异常
        'order_id': '201811281443459095006600',  # 平台订单ID
        'order_number': 'Test_395168',  # 我方订单ID
        'req_no': 'Test_395168', # 我方订单ID
    }
    """
    if response.status_code != 200:
        return
    order = withdraw_db.get_order(pay_id)
    data = json.loads(response.text)
    if str(data['op_ret_code']) != '000':
        _LOGGER.info('meifubao_withdraw withdraw fail: %s' % data)
        order.status = WITHDRAW_ORDER_STATUS.THIRD_FAIL
        order.extra_info = data
        order.save()
        return
    order.status = WITHDRAW_ORDER_STATUS.DONE
    order.extra_info = response.text
    order.save()
    if data.get('accountNumber'):
        bank_db.update_real_balance(data['accountNumber'], data['cardBalance'])
    _LOGGER.info("meifubao_withdraw success mch_id: %s, order_id: %s", order.mch_id, order.id)
    notify_success, _ = mch_handler.notify_mch_withdraw(order)
    if not notify_success:
        submit_notify_event(order, TIMER_EVENT_TYPE.MCH_WITHDRAW_NOTIFY)
